package com.heima.article.job;

import com.alibaba.fastjson.JSON;
import com.heima.article.service.HotArticleService;
import com.heima.model.common.constants.article.HotArticleConstants;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.model.mess.app.UpdateArticleMess;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.stream.Collectors;

@Component
@Slf4j
public class UpdateHotArticleJob {
    @Autowired
    StringRedisTemplate redisTemplate;

    @Autowired
    HotArticleService hotArticleService;

    @XxlJob("updateHotArticleJob")
    public ReturnT handle(String params){
        // 1. 先获取redis中的行为数据
        List<UpdateArticleMess> articleMessList = getUpdateArticleMesses();
        if (CollectionUtils.isEmpty(articleMessList)) {
            log.info("太冷清了，没人访问我们的文章~~~~");
            return ReturnT.SUCCESS;
        }
        // 2. 按照文章id对行为数据进行分组 [{articleId:1,add:1,type:view}{articleId:1,add:1,type:likes}]   ==>  {articleId:1, view:1,like:1}
        List<ArticleVisitStreamMess> waitUpdateArticleScoreData = getWaitUpdateArticleScore(articleMessList);
        if (CollectionUtils.isEmpty(waitUpdateArticleScoreData)) {
            log.info("太冷清了，没人访问我们的文章~~~~");
            return ReturnT.SUCCESS;
        }
        // 3. TODO 修改文章分值   {articleId:1, view:1,like:1}
        waitUpdateArticleScoreData.forEach(hotArticleService::updateApArticle); // 调用hotArticleService 修改 每个文章的热度值
        return ReturnT.SUCCESS;
    }

    private List<ArticleVisitStreamMess> getWaitUpdateArticleScore(List<UpdateArticleMess> articleMessList) {
        List<ArticleVisitStreamMess> waitUpdateArticleScoreData = new ArrayList<>();
        // 1. 按照文章id分组   key: 文章id   value: 文章行为数据List
        Map<Long, List<UpdateArticleMess>> messByGroup =
                articleMessList.stream().collect(Collectors.groupingBy(UpdateArticleMess::getArticleId));
        // key 文章id 1    value: List<> 3
        // key 文章id 2    value: List<> 3
        // 2. 遍历分组   对每个分组进行聚合运算 ，  每个分组统计出一个 ArticleVisitStreamMess
        messByGroup.forEach((articleId,messList)->{
            // 按照文章分组   对每个分组进行聚合运算 ，  每个分组统计出一个 ArticleVisitStreamMess
            Optional<ArticleVisitStreamMess> reduce = messList.stream()
                    .map(updateMess -> {
                        // 映射 将每一条行为数据 都转化为  ArticleVisitStreamMess聚合对象
                        ArticleVisitStreamMess visitStreamMess = new ArticleVisitStreamMess();
                        visitStreamMess.setArticleId(articleId); // 设置文章id
                        switch (updateMess.getType()) {
                            case LIKES: // 点赞
                                visitStreamMess.setLike(updateMess.getAdd());
                                break;
                            case COLLECTION: // 收藏
                                visitStreamMess.setCollect(updateMess.getAdd());
                                break;
                            case COMMENT: //评论
                                visitStreamMess.setComment(updateMess.getAdd());
                                break;
                            case VIEWS: // 阅读
                                visitStreamMess.setView(updateMess.getAdd());
                                break;
                        }
                        return visitStreamMess;
                    }).reduce(new BinaryOperator<ArticleVisitStreamMess>() {
                        /**
                         * 归并运算  将流中的数据进行两两运算
                         * @param a1
                         * @param a2
                         * @return
                         */
                        @Override
                        public ArticleVisitStreamMess apply(ArticleVisitStreamMess a1, ArticleVisitStreamMess a2) {
                            a1.setLike(a1.getLike() + a2.getLike());
                            a1.setComment(a1.getComment() + a2.getComment());
                            a1.setCollect(a1.getCollect() + a2.getCollect());
                            a1.setView(a1.getView() + a2.getView());
                            return a1;
                        }
                    });
            waitUpdateArticleScoreData.add(reduce.get());
        });
        return waitUpdateArticleScoreData;
    }

    private List<UpdateArticleMess> getUpdateArticleMesses() {

        // 1.1    size 得到redis中有多少条数据
        ListOperations<String, String> listOper = redisTemplate.opsForList();
        Long size = listOper.size(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST);
        // 管道命令的多个返回结果   get(1)    get(2)
        List result = redisTemplate.executePipelined(new RedisCallback<List<UpdateArticleMess>>() {
            @Override
            public List<UpdateArticleMess> doInRedis(RedisConnection connection) throws DataAccessException {
                connection.openPipeline(); // 开启管道命令
                // 1.2 lrange (0,size-1)   ltrim ( size , -1 )  获取redis中的数据
                connection.lRange(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), 0, size - 1);
                // 1.3 ltrim 截断数据
                connection.lTrim(HotArticleConstants.HOT_ARTICLE_SCORE_BEHAVIOR_LIST.getBytes(), size, -1);
                return null;
            }
        }, RedisSerializer.string());
        if(CollectionUtils.isNotEmpty(result)){
            List<String> list = (List<String>)result.get(0);
            return list.stream().map(jsonStr-> JSON.parseObject(jsonStr,UpdateArticleMess.class)).collect(Collectors.toList());
        }
        return null;
    }


    public static void main(String[] args) {
        List<UpdateArticleMess> articleMessList = new ArrayList<>();
        UpdateArticleMess mess = new UpdateArticleMess();
        mess.setAdd(1);
        mess.setArticleId(1L);
        mess.setType(UpdateArticleMess.UpdateArticleType.LIKES);
        articleMessList.add(mess);
        UpdateArticleMess mess2 = new UpdateArticleMess();
        mess2.setAdd(1);
        mess2.setArticleId(1L);
        mess2.setType(UpdateArticleMess.UpdateArticleType.COMMENT);
        articleMessList.add(mess2);

        Map<Long, List<UpdateArticleMess>> messByGroup =
                articleMessList.stream().collect(Collectors.groupingBy(UpdateArticleMess::getArticleId));

        System.out.println(messByGroup);
    }
}
